clr.AddReference('System')
clr.AddReference('Kingdee.BOS')
from System import AppDomain
from System.IO import FileInfo
from System.Reflection import Assembly
from System.Reflection import BindingFlags
from System.CodeDom.Compiler import CompilerParameters
from Microsoft.CSharp import CSharpCodeProvider
from Kingdee.BOS.Core.Util import MD5Compute
from Kingdee.BOS.Cache import KCacheManagerFactory

refDlls = '''
Kingdee.BOS
Kingdee.BOS.Core
Kingdee.BOS.DataEntity
Kingdee.BOS.ServiceHelper
Microsoft.CSharp
System
System.Core
System.Data
System.Data.DataSetExtensions
System.Xml
System.Xml.Linq
Zy.K3Cloud.ToolbarCreateFactoryNumber
'''

code='''
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kingdee.BOS.Core.Bill.PlugIn;
using Kingdee.BOS.Core.Bill.PlugIn.Args;
using Kingdee.BOS.Core.DynamicForm;
using Kingdee.BOS.Core.DynamicForm.PlugIn;
using Kingdee.BOS.Core.DynamicForm.PlugIn.Args;
using Kingdee.BOS.Orm.DataEntity;
using Kingdee.BOS.Core.Metadata;
using Kingdee.BOS.Core.Metadata.EntityElement;
using Kingdee.BOS.ServiceHelper;
using Kingdee.BOS.Util;
using System.ComponentModel;

namespace Zy.K3Cloud.ToolbarCreateFactoryNumber
{
    /// <summary>
    /// 【表单插件】点击生成出厂编码
    /// </summary>
    [Description("【表单插件】点击生成出厂编码"), HotUpdate]
    public class ToolbarListPlugIn : AbstractDynamicFormPlugIn
    {
        /// <summary>
        /// 将数值的字符串数组按区间分段
        /// </summary>
        /// <param name="arr"></param>
        /// <returns></returns>
        private static string CompareNumber(string[] arr)
        {
            int[] arr2 = new int[arr.Length];
            bool isZero = arr[0][0] == '0';
            for (int i = 0; i < arr.Length; i++)
            {
                arr2[i] = Convert.ToInt32(arr[i]);
            }

            var query = arr2.OrderBy(p => p).Aggregate<int, List<List<int>>>(null, (m, n) =>
            {
                if (m == null) return new List<List<int>>() { new List<int>() { n } };
                if (m.Last().Last() != n - 1)
                {
                    m.Add(new List<int>() { n });
                }
                else
                {
                    m.Last().Add(n);
                }
                return m;
            });
            //打印结果
            string rs = "";

            query.ForEach(p => rs = (p.First() == p.Last()) ? (isZero ? "0" : "") + p.First() : ((isZero ? "0" : "") + p.First() + (isZero ? "-0" : "-") + p.Last())); 
            return rs;
        }

        /// <summary>
        /// 检查出厂编码与销售数量容量是否相符
        /// </summary>
        /// <returns>>0：销售数量减小；<0：销售数量增多; =0：销售没变或编码为空</returns>
        private static int CheckCount(DynamicObject entityObj)
        {
            string fn = entityObj["F_HY_Text1"] != null ? entityObj["F_HY_Text1"].ToString() : ""; //出厂编码字段
            int count = Convert.ToInt32(entityObj["Qty"]); //销售数量
            int fn_count;
            if (fn != "")
            {
                String[] fns = fn.Split('-');
                if (fns.Length == 1)
                {
                    fn_count = 1;
                }
                else if (fns.Length == 2)
                {
                    fn_count = Convert.ToInt32(fns[1]) - Convert.ToInt32(fns[0]) + 1;
                }
                else
                {
                    //编码有多个-符号，此种情况按不相符处理
                    fn_count = -999;
                }

                return fn_count - count; //编码容量与销售数量不相符则提示需要更新条码；
            }else
            {
                return 0;
            }
        }

        /// <summary>
        /// 分录中手工点击创建出厂编码
        /// </summary>
        /// <param name="e"></param>
        public override void BarItemClick(BarItemClickEventArgs e)
        {
            base.BarItemClick(e);
            if (e.BarItemKey.Equals("ora_CreateFactoryNumber", StringComparison.OrdinalIgnoreCase))
            {
                var entity = this.View.BillBusinessInfo.GetEntity("FSaleOrderEntry");
                var entityObjs = this.View.Model.GetEntityDataObject(entity);
                if (entityObjs != null)
                {
                    int index = 0;
                    String skipped = ""; //保存被跳过编码的行号
                    String coded = ""; //保存被编码的行号

                    foreach (var entityObj in entityObjs)
                    {
                        //遍历单据体数据包
                        //按行生成出厂编码
                        //设置单据体的当前行
                        this.Model.SetEntryCurrentRowIndex("FSaleOrderEntry", index);

                        string ruleId = ""; //存储编码规则内码
                        string fntype = entityObj["F_ora_fn_type"] != null ? entityObj["F_ora_fn_type"].ToString() : "";
                        int count = Convert.ToInt32(entityObj["Qty"]); //销售数量
                        var Material = (DynamicObject)entityObj["MaterialId"];//物料对象

                        if (count == 0 || Material == null)
                        {
                            skipped = (skipped == "") ? Convert.ToString(index) : skipped + "," + Convert.ToString(index);
                            break; //销售数量为0或没填物料编码的跳过。
                        }

                        string materialId = Material["Number"].ToString();//物料编码
                        string fntxt = entityObj["F_HY_Text1"]!= null ? entityObj["F_HY_Text1"].ToString() : ""; //出厂编码字段
                        string materialName = Material["Name"].ToString(); //名称

                        //获取单据体元数据
                        EntryEntity entry = this.View.BillBusinessInfo.GetEntryEntity("FSaleOrderEntry");
                        //获取指定一行的单据体数据
                        DynamicObject entity_obj = this.Model.GetEntityDataObject(entry, index);
                        //获取子单据体元数据
                        SubEntryEntity sub_entry = this.View.BillBusinessInfo.GetEntryEntity("F_ora_SubEntity") as SubEntryEntity;
                        //出厂编码子单据体的总行数
                        int subRowCounts = this.Model.GetEntryRowCount("F_ora_SubEntity");

                        if (fntype == "")
                        {
                            //this.View.ShowErrMessage(string.Format("第 {0} 行未选择物料类别！", Convert.ToString(index+1)), "物料类别错误");
                            //return;
                            coded = (coded == "") ? Convert.ToString(index) : coded + "," + Convert.ToString(index);
                            break; //物料编码没填的行跳过。
                        }

                        bool executeReCode = true;
                        if( fntxt != "" )
                        {
                            int compareResult = CheckCount(entityObj);
                            if (compareResult > 0){
                                //编码容量比销售量大，销售数量减小；
                                //从分录中减少编码，从最大的开始；
                                //减小编码范围
                                //删除编号表中多余的行
                                if (subRowCounts > count)
                                {
                                    for (int KK = count; KK < subRowCounts; KK++)
                                    {
                                        this.Model.DeleteEntryRow("F_ora_SubEntity", KK); //key为单据体标识
                                    }
                                    this.View.UpdateView("F_ora_SubEntity");
                                }
                                String[] rfn = new string[count];
                                for (int rk = 0; rk < count; rk++)
                                {
                                    rfn[rk] = this.View.Model.GetValue("F_ora_sub_Fn", rk).ToString();
                                }
                                this.Model.SetItemValueByID("F_HY_Text1", CompareNumber(rfn), index);
                                executeReCode = false; //调整编码范围完成，无需重新生成；
                                coded = (coded == "") ? Convert.ToString(index) : coded + "," + Convert.ToString(index);
                            }
                            else if(compareResult < 0)
                            {
                                //编码容量比销售量小，销售数量变大，需全部重新生成新码;
                                executeReCode = true;
                            }
                            else
                            {
                                //编码容量与销售量相符，询问是否生成编码；
                                //this.View.ShowMessage(string.Format("第 {0} 行 {1} 的出厂编码已存在，并且编码数与销售数量也相符，需要重新生成新编码么？", Convert.ToString(index + 1), materialModel), MessageBoxOptions.YesNo, result =>
                                //{
                                //    executeReCode = result == MessageBoxResult.Yes;
                                //});
                                skipped = (skipped == "") ? Convert.ToString(index) : skipped + "," + Convert.ToString(index);
                                executeReCode = false ; //编码存在，无需重新生成的跳过
                            }
                        }

                        if (executeReCode == false)
                        {
                            index++;
                            continue;
                        }
                        switch (fntype)
                        {
                            ///SELECT a.FRULEID AS 编码规则内码,b.FNAME AS 编码规则名称,a.* FROM T_BAS_BILLCODERULE a LEFT JOIN T_BAS_BILLCODERULE_L b ON a.FRULEID=b.FRULEID AND b.FLOCALEID=2052 WHERE a.FBILLFORMID='SAL_SaleOrder' AND b.FNAME='采购订单编码规则AH'
                            case "1": //YT
                                ruleId = "625d1a1da043ac";
                                break;
                            case "2": //CD
                                ruleId = "625d1a58a043ca";
                                break;
                            case "3": //调节板
                                ruleId = "625d1a6ea043ed";
                                break;
                            case "4": //特殊
                                ruleId = "625d1a7ea04430";
                                break;
                            default:
                                ruleId = "557561cf8ca005";
                                break;
                        }

                        var field = this.View.BillBusinessInfo.GetField("F_HY_Text1");//出厂编码

                        string[] fn = new string[count];


                        //删除编号表中多余的行
                        if( subRowCounts > count)
                        {
                            for (int KK = count; KK < subRowCounts; KK++)
                            {
                                this.Model.DeleteEntryRow("F_ora_SubEntity", KK); //key为单据体标识
                            }
                            this.View.UpdateView("F_ora_SubEntity");
                        }

                        for (int i = 0; i < count; i++)
                        {
                            var factoryNumber = BusinessDataServiceHelper.GetBillNoByField(this.Context, this.View.BillBusinessInfo, new[] { this.Model.DataObject }, true, ruleId, field, null);
                            if (factoryNumber != null && factoryNumber.Count > 0)
                            {
                                fn[i] = factoryNumber[0].BillNo; 
                                //创建新的子单据体分录行
                                this.Model.CreateNewEntryRow(entity_obj, sub_entry, -1);
                                
                                //子单据体行赋值用
                                this.View.Model.SetValue("F_ora_sub_Fn", fn[i], i);
                                this.View.Model.SetValue("F_ora_FnMemo", "", i);
                             //   this.View.Model.SetValue("F_ora_Text", fn[i], i);
                             //   this.View.Model.SetValue("F_ora_Text1", "", i);
                                this.View.Model.SetValue("FBILLNO", ""); //获取编码后要清空表单编号字段，便于下次获取。
                                this.View.UpdateView("F_ora_SubEntity");
                            }
                        }
                        this.Model.SetItemValueByID("F_HY_Text1", CompareNumber(fn), index);
                        index++;
                    }
                    this.View.ShowMessage(string.Format("成功生成出厂编码的行号为：{0}; 因未选编码类别、未填销售数量、未填物料编码或已存在无需调整跳过的行号为：{1}", coded, skipped), MessageBoxOptions.OK);
                }
            }
        }
    }
}
'''

def CompileCode(code, refDlls):	#动态编译C#代码，放入缓存
	refAsmNames = filter(lambda y: y <> '', map(lambda x: x.strip(), refDlls.split()))
	try:
		kcmgr = KCacheManagerFactory.Instance.GetCacheManager('PyCodeGeneratorCache', 'PyCodeGeneratorCache')
	except:
		return None
	cacheKey = MD5Compute().MDString(code + '-'.join(refAsmNames))
	if(kcmgr.Get(cacheKey) is not None):
		return kcmgr.Get(cacheKey)
	cSharpCodePrivoder = CSharpCodeProvider({'CompilerVersion':'v4.0'})
	codeCompiler = cSharpCodePrivoder.CreateCompiler()
	compilerParameters = CompilerParameters()
	compilerParameters.GenerateExecutable = False; 
	compilerParameters.GenerateInMemory = True; 
	for refAsmName in refAsmNames:
		asms = filter(lambda asm: asm.GetName().Name == refAsmName, AppDomain.CurrentDomain.GetAssemblies())
		if(len(asms) > 0):
			compilerParameters.ReferencedAssemblies.Add(FileInfo(asms[0].CodeBase.Substring(8)).DirectoryName.Replace('\\', '/')+'/' + refAsmName + '.dll')
		else:
			try:
				compilerParameters.ReferencedAssemblies.Add(FileInfo(Assembly.Load(refAsmName).CodeBase.Substring(8)).DirectoryName.Replace('\\', '/') + '/' + refAsmName + '.dll');
			except:
				pass
		compilerResults = codeCompiler.CompileAssemblyFromSource(compilerParameters, code);
	if (compilerResults.Errors.HasErrors):
		raise Exception('\r\n'.join(map(lambda err: err.ErrorText, compilerResults.Errors)))
	compiledAsm = compilerResults.CompiledAssembly;
	kcmgr.Put(cacheKey, compiledAsm)
	return compiledAsm

assembly = CompileCode(code, refDlls)
csPlugin = assembly.CreateInstance('Zy.K3Cloud.ToolbarCreateFactoryNumber.ToolbarListPlugIn') if assembly is not None else None

def getPlugIn():
	csPlugin.SetContext(this.Context, this.View)
	return csPlugin

def BarItemClick(e):
    getPlugIn().BarItemClick(e)

